<?php
require_once 'Zend/Db/Tree/Adapter/AdapterAbstract.php';

class Zend_Db_Tree_Adapter_NestedSet extends Zend_Db_Tree_Adapter_AdapterAbstract
{
    protected $_pk = 'id';

    protected $_left = 'lft';

    protected $_right = 'rgt';

    protected $_level = 'lvl';

    public function getRootNode()
    {
        $select = $this->_select();
        $select->from($this->_getTableName())->where($this->_left . ' = 1');
        return $this->_fetchRow($select);
    }

    public function rootExists()
    {
        return (bool) $this->_db->fetchOne('SELECT 1 FROM ' . $this->_db->quoteIdentifier($this->_getTableName()) . ' WHERE ' . $this->_left . ' = 1');
    }

    public function createRoot(array $data = array())
    {
        if ($this->rootExists()) {
            require_once 'Zend/Db/Tree/Adapter/Exception.php';
            throw new Zend_Db_Tree_Adapter_Exception('Root node already exists');
        }
        $data = array_merge($data, array($this->_left => 1, $this->_right => 2, $this->_level => 0));
        $tableName = $this->_getTableName();
        $this->_db->beginTransaction();
        $this->_db->insert($tableName, $data);
        $pk = $this->_db->lastInsertId($tableName, $this->_pk);
        $this->_db->commit();
        return $pk;
    }

    public function getNode($nodeId)
    {
        $select = $this->_select();
        $select->from($this->_getTableName())->where($this->_pk . ' = ?', $nodeId);
        return $this->_fetchRow($select);
    }

    public function getChildrenCount($nodeId)
    {
        $nodeInfo = $this->_getNodeInformation($nodeId);
        if (null === $nodeInfo) {
            return 0;
        }
        $sql = 'SELECT COUNT(*) FROM ' . $this->_db->quoteIdentifier($this->_getTableName())
            . " WHERE {$nodeInfo[$this->_left]} < {$this->_left} AND {$nodeInfo[$this->_right]} > {$this->_right}";
        return (int) $this->_db->fetchOne($sql);
    }

    /**
     * @return array
     */
    public function getChildNodes($nodeId, $depth = -1)
    {
        $nodeInfo = $this->_getNodeInformation($nodeId);
        if (null === $nodeInfo) {
            return array();
        }
        $select = $this->_select();
        $select->from($this->_getTableName())
               ->where("{$nodeInfo[$this->_left]} < {$this->_left} AND {$nodeInfo[$this->_right]} > {$this->_right}")
               ->order($this->_left);
        if ($depth != -1) {
            $select->where("{$this->_level} <= " . ($nodeInfo[$this->_level] + intval($depth)));
        }
        return $this->_fetchAll($select);
    }

    /**
     * @return null|array
     */
    protected function _getNodeInformation($nodeId)
    {
        $sql = "SELECT {$this->_left}, {$this->_right}, {$this->_level}
            FROM " . $this->_getTableName() . " WHERE {$this->_pk} = ?";
        $result = $this->_db->fetchRow($sql, $nodeId, Zend_Db::FETCH_ASSOC);
        return $result ? $result : null;
    }
}
